package com.winit.schedule.job.tracker.repository.mysql;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import com.winit.schedule.core.repository.entity.*;
import com.winit.schedule.core.repository.query.*;
import com.winit.schedule.job.tracker.repository.mysql.dao.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.winit.schedule.core.model.JobID;
import com.winit.schedule.core.repository.constants.JobStatus;
import com.winit.schedule.core.repository.constants.LogType;
import com.winit.schedule.core.repository.constants.TaskItemStatus;
import com.winit.schedule.core.repository.constants.TaskStatus;
import com.winit.schedule.core.utils.Clock;
import com.winit.schedule.core.utils.DateUtils;
import com.winit.schedule.core.utils.StringUtils;
import com.winit.schedule.job.tracker.repository.ConsoleDataManager;
import com.winit.schedule.job.tracker.repository.mysql.plugin.SearchRequest;

@Service("consoleDataManager")
@Transactional
public class MysqlConsoleDataManager extends MysqlLogDataManager implements ConsoleDataManager {

    private static transient final Logger logger = LoggerFactory.getLogger(MysqlConsoleDataManager.class);

    @Resource
    private MysqlTaskDao                  taskDao;

    @Resource
    private MysqlTaskItemDao              taskItemDao;

    @Resource
    private MysqlTaskTrackerDao           taskTrackerDao;

    @Resource
    private MysqlJobDao                   jobDao;

    @Resource
    private MysqlLogDao                   logDao;

    @Resource
    private MysqlTaskRunRecordDao         taskRunRecordDao;

    @Resource
    private MysqlTaskDefineDao            taskDefineDao;

    @Resource
    private MysqlUserDao userDao;

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<TaskEntity> findTaskEntity(TaskQuery query) {
        List<TaskEntity> entitys = taskDao.findTasks(query);
        return entitys;
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Map<String, Object> queryTaskRunDetails(String taskId) {
        Map<String, Object> taskRunDetail = new HashMap<String, Object>();
        TaskEntity task = taskDao.getTaskById(taskId);
        taskRunDetail.put("task", task);

        List<TaskItemEntity> taskItems = taskItemDao.getTaskItemsByTaskId(taskId);
        taskRunDetail.put("taskItems", taskItems);

        TaskTrackerQuery query = new TaskTrackerQuery();
        query.setTaskGroup(task.getTaskGroup());
        query.setTaskName(task.getTaskName());
        List<TaskTrackerEntity> taskTrackers = taskTrackerDao.findTaskTrackerByTask(query);
        taskRunDetail.put("taskTrackers", taskTrackers);
        return taskRunDetail;
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<TaskTrackerEntity> queryTaskTrackers(TaskTrackerQuery query) {
        SearchRequest<TaskTrackerQuery> search = new SearchRequest<TaskTrackerQuery>(1, Integer.MAX_VALUE);
        search.setParameterEntity(query);
        return taskTrackerDao.findTaskTrackerPage(search);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public TaskEntity getTaskById(String taskId) {
        return taskDao.getTaskById(taskId);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public String saveTask(TaskEntity task) {
        try {
            List<TaskItemEntity> taskItems = new ArrayList<TaskItemEntity>();
            if (null != task) {
                taskItemDao.removeTaskItemByTaskId(task.getTaskId().getTaskId());
                if (StringUtils.isBlank(task.getTaskItem())) {
                    if (!StringUtils.isBlank(task.getRunStartTime())) {
                        taskItems.add(new TaskItemEntity(task.getTaskId(),
                            "",
                            task.isImmediatelyRun() ? Clock.getSystemTime() : DateUtils.getNextValidTimeAfter4Cron(task.getRunStartTime(),
                                Clock.getCurrentDate()),
                            task.isAutoRecovery()));
                    } else {
                        taskItems.add(new TaskItemEntity(task.getTaskId(), "", task.isAutoRecovery()));
                    }
                } else {
                    Collection<String> params = StringUtils.getTrimmedStringCollection(task.getTaskItem());
                    // taskItemDao.removeTaskItemByTaskId(task.getTaskId().getTaskId());
                    if (!StringUtils.isBlank(task.getRunStartTime())) {
                        for (String param : params) {
                            if (!StringUtils.isBlank(param)) {
                                taskItems.add(new TaskItemEntity(task.getTaskId(),
                                    param,
                                    task.isImmediatelyRun() ? Clock.getSystemTime() : DateUtils.getNextValidTimeAfter4Cron(task.getRunStartTime(),
                                        Clock.getCurrentDate()),
                                    task.isAutoRecovery()));
                            }
                        }
                    } else {
                        for (String param : params) {
                            if (!StringUtils.isBlank(param)) {
                                taskItems.add(new TaskItemEntity(task.getTaskId(), param, task.isAutoRecovery()));
                            }
                        }
                    }
                }
            }

            for (TaskItemEntity taskItem : taskItems) {
                taskItemDao.saveTaskItem(taskItem);
            }
            // 任务由tasktracker注册（新增），console只对已注册任务的属性作修改
            taskDao.updateTask(task);
            logger(task, LogType.UPDATE_TASK);
        } catch (Throwable e) {
            logger.error("Save task and taskItem failure, Delete all taskItems. task id is : "
                         + task.getTaskId().getTaskId(), e);
            return e.getMessage();
        }
        return null;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void updateTaskStatus(String taskId, TaskStatus status) {
        taskDao.updateTaskStatusByTaskId(taskId, status);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public long findRunningTaskItemNumByTaskId(String taskId) {
        return taskItemDao.findRunningTaskItemNumByTaskId(taskId);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<JobEntity> queryAllJob(JobQuery job) {
        return jobDao.queryAllJobs(job);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void saveJob(JobEntity job, List<String> taskIds) throws ParseException {
        boolean isUpdate = false;
        if (job.getJobId() == null) {
            job.setJobId(new JobID(DateUtils.format(Clock.getCurrentDate(), DateUtils.YYYYMMDDHHMMSS)));
        } else {
            isUpdate = true;
            taskDao.delJobIdForTask(job.getJobId().getJobId());
        }
        if (job.getJobStatus() == null) {
            job.setJobStatus(JobStatus.INIT);
        }
        long currentTime = Clock.getSystemTime();
        if (job.getCreateTime() == 0) {
            job.setCreateTime(currentTime);
        }
        job.setLastUpdateTime(currentTime);
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("jobId", job.getJobId());
        params.put("taskIds", taskIds);
        params.put("nextRunTime",
            job.isImmediatelyRun() ? Clock.getSystemTime() : DateUtils.getNextValidTimeAfter4Cron(job.getRunStartTime(),
                Clock.getCurrentDate()));
        taskItemDao.updateNextRunTime(params);

        params = new HashMap<String, Object>();
        params.put("jobId", job.getJobId().getJobId());
        params.put("runStartTime", job.getRunStartTime());
        params.put("runEndTime", job.getRunEndTime());
        params.put("taskIds", taskIds);
        params.put("immediatelyRun", job.isImmediatelyRun());
        taskDao.updateJobIdAndRunTimeForTask(params);

        JobEntity oldJob = jobDao.getJobById(job.getJobId().getJobId());
        if (oldJob == null) {
            jobDao.saveJob(job);
        } else {
            jobDao.updateJob(job);
        }
        logger(job, taskIds, isUpdate);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void startJob(JobID jobId) {
        jobDao.updateJobStatus(jobId.getJobId(), JobStatus.START);
        taskDao.updateTaskStatusByJobId(jobId.getJobId(), TaskStatus.START);
        logger(jobId, LogType.START_JOB);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void stopJob(JobID jobId) {
        jobDao.updateJobStatus(jobId.getJobId(), JobStatus.STOP);
        taskDao.updateTaskStatusByJobId(jobId.getJobId(), TaskStatus.STOP);
        logger(jobId, LogType.STOP_JOB);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void delJob(JobID jobId) {
        taskDao.delJobIdForTask(jobId.getJobId());
        jobDao.removeJob(jobId.getJobId());
        logger(jobId, LogType.DELETE_JOB);

    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public JobEntity queryJobById(String jobId) {
        return jobDao.getJobById(jobId);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<TaskEntity> queryTasksNotRelatedJob() {
        return taskDao.queryTasksNotRelatedJob();
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<TaskEntity> queryTasksRelatedJob(JobID jobId) {
        return taskDao.queryTasksRelatedJob(jobId.getJobId());
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public long findRunningTaskItemNumByJobId(JobID jobId) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("jobId", jobId.getJobId());
        params.put("taskItemStatus", TaskItemStatus.RUN_STATUS);
        return taskItemDao.findRunningTaskItemNumByJobId(params);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Page queryTaskTrackerLog(String taskTrackerUuid, long startDate, long endDate, int page, int rows) {
        SearchRequest<LogQuery> search = new SearchRequest<LogQuery>(page, rows);
        LogQuery query = new LogQuery();
        query.setHasTaskTracker(true);
        query.setStartTime(startDate);
        query.setEndTime(endDate);
        query.setLogTypes(Arrays.asList(new LogType[] { LogType.TASKTRACKER_CONNCET, LogType.TASKTRACKER_DISCONNECT }));
        search.setParameterEntity(query);
        List<LogEntity> data = logDao.queryLogPage(search);
        long count = logDao.queryLogPageCount(query);
        return new Page(search.getPageNo(), count, data);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Page queryJobLog(UserLogQuery userLogQuery, int page, int rows) {
        SearchRequest<UserLogQuery> search = new SearchRequest<UserLogQuery>(page, rows);
        UserLogQuery query = new UserLogQuery();
        query.setLogTypes(Arrays.asList(LogType.JOB_LOG));
        query.setJobId(userLogQuery.getJobId());
        query.setOperator(null != userLogQuery.getOperator() ? userLogQuery.getOperator().trim() : null);
        if (!StringUtils.isBlank(userLogQuery.getTaskId())) {
            query.setHasTaskId(true);
            query.setTaskId(userLogQuery.getTaskId());
        }
        query.setStartTime(userLogQuery.getStartTime());
        query.setEndTime(userLogQuery.getEndTime());
        search.setParameterEntity(query);
        List<UserLogEntity> data = userLogDao.queryLogPage(search);
        long count = userLogDao.queryLogPageCount(query);
        return new Page(search.getPageNo(), count, data);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Page queryTaskTrackerDisconnectLog(String ip, long startDate, long endDate, int page, int rows) {
        SearchRequest<LogQuery> search = new SearchRequest<LogQuery>(page, rows);
        LogQuery query = new LogQuery();
        query.setLogType(LogType.TASKTRACKER_DISCONNECT);
        query.setIp(ip);
        query.setStartTime(startDate);
        query.setEndTime(endDate);
        search.setParameterEntity(query);
        List<LogEntity> data = logDao.queryLogPage(search);
        long count = logDao.queryLogPageCount(query);
        return new Page(search.getPageNo(), count, data);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Page queryTaskExeFailureLog(String jobId, String taskId, long startDate, long endDate, int page, int rows) {
        SearchRequest<LogQuery> search = new SearchRequest<LogQuery>(page, rows);
        LogQuery query = new LogQuery();
        query.setLogTypes(Arrays.asList(new LogType[] { LogType.TASK_EXE_FAILURE, LogType.TASK_RUNNER_FAILURE }));
        query.setJobId(jobId);
        query.setTaskId(taskId);
        query.setStartTime(startDate);
        query.setEndTime(endDate);
        search.setParameterEntity(query);
        List<LogEntity> data = logDao.queryLogPage(search);
        long count = logDao.queryLogPageCount(query);
        return new Page(search.getPageNo(), count, data);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Page queryTaskLog(LogQuery query) {
        if (null == query) {
            query = new LogQuery();
        }
        SearchRequest<LogQuery> search = new SearchRequest<LogQuery>(query.getOffset(), query.getLimit());
        query.setHasTaskId(true);
        search.setParameterEntity(query);
        List<LogEntity> data = logDao.queryLogPage(search);
        long count = logDao.queryLogPageCount(query);
        return new Page(search.getPageNo(), count, data);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Page queryTaskRunRecord(String jobId, String taskId, long startDate, long endDate, int page, int rows) {
        SearchRequest<TaskRunRecordQuery> search = new SearchRequest<TaskRunRecordQuery>(page, rows);
        TaskRunRecordQuery query = new TaskRunRecordQuery();
        query.setJobId(jobId);
        query.setTaskIdStr(taskId);
        query.setStartTime(startDate);
        query.setEndTime(endDate);
        search.setParameterEntity(query);
        List<TaskRunRecordEntity> data = taskRunRecordDao.getTaskRunRecordPage(search);
        long count = taskRunRecordDao.getTaskRunRecordPageCount(query);
        return new Page(search.getPageNo(), count, data);

    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public List<TaskItemEntity> queryTaskItemsByTimeout(TaskItemQuery query) {
        return taskItemDao.queryTaskItemsByTimeout(query);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void recoveryTaskItemByIds(long timeout, String[] ids) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("timeout", timeout);
        params.put("ids", ids);
        params.put("taskItemStatus", TaskItemStatus.RUN_STATUS);
        taskItemDao.recoveryTaskItemByTimeoutAndIds(params);

        logger(ids, LogType.RECOVERY_TASK_ITEM);
    }

    @Override
    public List<TaskItemEntity> queryTaskItemsForLongtime(TaskItemQuery query) {
        return taskItemDao.queryTaskItemsForLongtime(query);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void removeTaskTrackerAndTaskDefines(String taskTrackerUuid) {
        taskTrackerDao.removeTaskTracker(taskTrackerUuid);
        taskDefineDao.removeTaskDefineByTaskTrackerUuid(taskTrackerUuid);
    }

    @Override
    public boolean hasTaskItem(String taskId) {
        List<TaskItemEntity> items = taskItemDao.getTaskItemsByTaskId(taskId);
        return null != items && items.size() > 0;
    }

    @Override public Page queryUserPage(UserQuery query, int page, int rows) {
        SearchRequest<UserQuery> search = new SearchRequest<UserQuery>(page, rows);
        search.setParameterEntity(query);
        List<UserEntity> data = userDao.queryUserPage(search);
        long count = userDao.queryUserPageCount(query);
        return new Page(search.getPageNo(), count, data);
    }

    @Override public UserEntity queryUserByUsername(String username) {
        return userDao.findByUsername(username);
    }

    @Override public void updateUserPws(String username, String password) {
        if (!StringUtils.isBlank(username) && ! StringUtils.isBlank(password)) {
            UserEntity user = new UserEntity();
            user.setUsername(username);
            user.setPassword(password);
            userDao.updatePwd(user);
        }
    }

    @Override public UserEntity findByUsernameAndPassword(String username, String password) {
        UserEntity user = new UserEntity();
        user.setUsername(username);
        user.setPassword(password);
        return userDao.findByUsernameAndPwd(user);
    }

    @Override public void updateUser(UserEntity user) {
        userDao.updateUser(user);
    }

    @Override public void saveUser(UserEntity user) {
        userDao.saveUser(user);
    }
}
